package com.yjz.jvm.juc.ExecutorService;

import com.yjz.BaseJunit4Test;
import com.yjz.jvm.juc.ExecutorService.result.CalculationTaskResult;
import com.yjz.jvm.juc.ExecutorService.task.CalculationTask;
import com.yjz.jvm.juc.ExecutorService.vo.Student;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * tip:多核Cpu的时候真的是 多任务并行，单核只是交替运行（并发）
 * java并发之如何解决线程安全问题: https://www.cnblogs.com/w2154/p/6637717.html
 * 线程池深度学习
 * 总结：主函数退出后，子函数就不会执行了,
 * 所以主函数得在拿到子函数与结果之后才能退出,主线程不能future.get(),不然就是顺序执行了(阻塞就是同步了),多线程就毫无意义了,
 * 不使用future.get()的话，可以看见子线程的结果几乎是同时打印出来,说明子线程是并发执行的
 *
 * @author :yuanJinZhong
 * @description:
 * @date :2019/1/3-21:48
 */
@Slf4j
public class ExecutorDeepLearn extends BaseJunit4Test {
  @Autowired
  private ThreadPoolTaskExecutor taskExecutor;

  @Test
  public void taskExecutorTest() throws ExecutionException, InterruptedException, IOException {
    while (true) {

      long start = System.currentTimeMillis();

      List<List<Student>> studentList = this.dataInit();

      List<Future<CalculationTaskResult>> furterList = new ArrayList<>();

      for (List<Student> list : studentList) {
        Future<CalculationTaskResult> future = taskExecutor.submit(new CalculationTask(list));
        furterList.add(future);
        //此处不能get,get会阻塞,就和同步没有区别了
        //CalculationTaskResult result = future.get();
        // log.warn("任务的执行结果是: {} ", result.getTotalScore());

      }
      long blockStart = System.currentTimeMillis();
//            /**********************等待线程池任务执行完毕的正确姿势***************************************************/
//            //关闭提交任务到池子的通道,再等待池子里面的任务执行完成
//            taskExecutor.shutdown();
//        /*当前线程阻塞,直到:
//                    1:等所有已提交的任务（包括正在跑的和队列中等待的）执行完
//                    2:或者等超时时间到
//                    3:或者线程被中断，抛出InterruptedException
//        此处是第一种情况,等待所有任务执行完毕(不会超时哦,因为等待时间设置的非常长)*/
//            taskExecutor.getThreadPoolExecutor().awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
//            /*********************等待线程池任务执行完毕的正确姿势***************************************************/
      long blockEnd = System.currentTimeMillis();
      /********************主线程等待池子里面任务执行完毕(即阻塞等待)的偏方(也能达到效果)****************************************************************/
      boolean flag = true;
      //轮询future列表,以达到主线程等待线程池任务都执行完毕的目的
      while (flag) {
        for (Future<CalculationTaskResult> future : furterList) {
          try {
            CalculationTaskResult result = future.get(0, TimeUnit.SECONDS);
            if (null != result) {
              flag = false;
            }
          } catch (TimeoutException e) {
            //  该trace 不需要打印，因为没有获取到结果就肯定会报超时异常，但是这个异常是正常！报异常时我继续while true 就可以
            //e.printStackTrace();
            flag = true;
          }
        }
      }
      //若while(true) 执行结束, 则说明:线程池里所有任务都执行完毕,则可以从furter列表获取计算结果
      /********************主线程等待池子里面任务执行完毕(即阻塞等待)的偏方****************************************************************/

      int result = 0;
       /* 因为taskExecutor.getThreadPoolExecutor().awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
        这段代码会等待池里面的任务都执行完毕,所以此处future.get()不会阻塞，能立马拿到结果*/
      for (Future<CalculationTaskResult> future : furterList) {
        CalculationTaskResult futureResult = future.get();
        result += futureResult.getTotalScore();
      }
      long end = System.currentTimeMillis();
      log.warn("所有线程的计算结果总和为:{}", result);
      log.warn("主线程等待所有任务结束时阻塞了: {} 毫秒", blockEnd - blockStart);
      log.warn("主程序执行结束,耗时:{} 毫秒", (end - start));
    }
  }


  /**
   * init data
   *
   * @return
   */
  public List<List<Student>> dataInit() {
    List<List<Student>> bigList = new ArrayList<>();
    List<Student> list1 = new ArrayList<>();
    list1.add(new Student("张三", 1));
    list1.add(new Student("李四", 3));
    list1.add(new Student("王五", 6));
    List<Student> list2 = new ArrayList<>();
    list2.add(new Student("lucy", 10));
    list2.add(new Student("liLei", 5));
    list2.add(new Student("jack", 5));
    List<Student> list3 = new ArrayList<>();
    list3.add(new Student("one", 10));
    list3.add(new Student("two", 5));
    list3.add(new Student("three", 5));
    bigList.add(list1);
    bigList.add(list2);
    bigList.add(list3);
    return bigList;
  }


}
